package com.tian.mybatis.executor;

import com.tian.mybatis.config.Configuration;
import com.tian.mybatis.config.MapperStatement;
import com.tian.mybatis.util.ParameterMapping;
import com.tian.mybatis.util.ParameterMappingTokenHandler;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.*;

/**
 * @author tianwc  公众号：java后端技术全栈、面试专栏
 * @version 1.0.0
 * @date 2023年09月21日 16:55
 * 在线刷题 1200+题和1000+篇干货文章：<a href="https://woaijava.cc/">博客地址</a>
 * <p>
 * SQL执行器
 */
public class SimpleExecutor implements Executor {

    /**
     * 执行SQL的核心方法  执行JDBC
     *
     * @param configuration
     * @param mapperStatement
     * @param params
     * @param <E>
     * @return
     */
    @Override
    public <E> List<E> doQuery(Configuration configuration, MapperStatement mapperStatement, Object... params) throws Exception {
        //1：获取数据连接
        Connection connection = configuration.getDataSource().getConnection();
        System.out.println(connection);
        //2：获取要执行的SQL语句
        //select * from user where id=#{id}
        //select * from user where id=#{id} and userName=#{userName}

        //但是JDBC只认识 select * from user where id=?
        //select * from user where id=? and userName=?
        //不认识带#的参数

        //把带#参数转换成?

        String sql = mapperStatement.getSql();

        BondSql bondSql = getBoundSql(sql);

        PreparedStatement preparedStatement = connection.prepareStatement(bondSql.getSqlText());

        //参数类型 com.tian.test.User
        String parameterType = mapperStatement.getParameterType();

        Class<?> parameterClassType = this.getClassType(parameterType);
        List<String> parameterMappingList = bondSql.getParameterMappingList();

        for (int i = 0; i < parameterMappingList.size(); i++) {
            String content = parameterMappingList.get(i);
            //返回值是基本数据或基本类型封装类时，直接设置
            if (parameterClassType == Integer.class
                    || parameterClassType == String.class
                    || parameterClassType == Float.class
                    || parameterClassType == Double.class) {
                preparedStatement.setObject(i + 1, params[0]);
                continue;
            }
            Annotation[] annotations = params[0].getClass().getAnnotations();

            Field declaredField = parameterClassType.getDeclaredField(content);
            declaredField.setAccessible(true);
            Object data = declaredField.get(params[0]);
            preparedStatement.setObject(i + 1, data);
        }

        //执行SQL
        String id = mapperStatement.getId();
        ResultSet resultSet;
        //查询
        resultSet = preparedStatement.executeQuery();

        //com.tian.text.User
        String resultType = mapperStatement.getResultType();

        Class<?> resultTypeClass = this.getClassType(resultType);
        //查询结果列表
        List<Object> objectList = new ArrayList<>();
        while (resultSet.next()) {
            //调用无参构造方法生成对象
            Object o = resultTypeClass.newInstance();
            ResultSetMetaData metaData = resultSet.getMetaData();
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                String columnName = metaData.getColumnName(i);
                Object value = resultSet.getObject(columnName);
                //使用内省根据数据库表和实体类的属性和字段对应关系数据封装
               /* PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);
                Method writeMethod = propertyDescriptor.getWriteMethod();
                writeMethod.invoke(o, value);*/
                Field declaredField = resultTypeClass.getDeclaredField(columnName);
                declaredField.setAccessible(true);
                declaredField.set(o, value);

            }
            objectList.add(o);
        }

        return (List<E>) objectList;
    }

    @Override
    public <E> E doUpdate(Configuration configuration, MapperStatement mapperStatement, Object... params) throws Exception {
        //1：获取数据连接
        Connection connection = configuration.getDataSource().getConnection();
        //2：获取要执行的SQL语句
        //select * from user where id=#{id}
        //select * from user where id=#{id} and userName=#{userName}

        //但是JDBC只认识 select * from user where id=?
        //select * from user where id=? and userName=?
        //不认识带#的参数

        //把带#参数转换成?

        String sql = mapperStatement.getSql();

        BondSql bondSql = this.getBoundSql(sql);

        PreparedStatement preparedStatement = connection.prepareStatement(bondSql.getSqlText());

        //参数类型 com.tian.test.User
        String parameterType = mapperStatement.getParameterType();

        Class<?> parameterClassType = this.getClassType(parameterType);
        List<String> parameterMappingList = bondSql.getParameterMappingList();

        /**
         *   参数类型 com.tian.test.User
         *   mybatis中sql是select * from user where id=#{id}
         *
         */
        for (int i = 0; i < parameterMappingList.size(); i++) {
            String content = parameterMappingList.get(i);
            //返回值是基本数据或基本类型封装类时，直接设置
            if (parameterClassType == Integer.class
                    || parameterClassType == String.class
                    || parameterClassType == Float.class
                    || parameterClassType == Double.class) {
                preparedStatement.setObject(i + 1, params[0]);
                continue;
            }
            Field declaredField = parameterClassType.getDeclaredField(content);
            declaredField.setAccessible(true);
            Object data = declaredField.get(params[0]);
            preparedStatement.setObject(i + 1, data);
        }

        //执行SQL
        String id = mapperStatement.getId();
        ResultSet resultSet;
        //增删改 操作
        int result = preparedStatement.executeUpdate();
        List<Integer> integerList = new ArrayList<>();
        integerList.add(result);
        Integer rs = new Integer(result);
        return (E) rs;
    }

    private Class<?> getClassType(String parameterType) throws ClassNotFoundException {
        if (parameterType != null) {
            return Class.forName(parameterType);
        }
        return null;
    }

    static int findPosition = 0;
    static Map<Integer, Integer> map = new TreeMap<>();
    static List<String> parameterMappings = new ArrayList<>();

    /**
     * sql # 占位符 替换成 ?问号
     *
     * @param sql
     * @return
     */
    private static BondSql getBoundSql(String sql) {

        parserSql(sql);

        Set<Map.Entry<Integer, Integer>> entries = map.entrySet();
        for (Map.Entry<Integer, Integer> entry : entries) {
            Integer key = entry.getKey() + 2;
            Integer value = entry.getValue();

            parameterMappings.add(sql.substring(key, value));
        }
        for (String parameterMapping : parameterMappings) {
            sql = sql.replace("#{" + parameterMapping + "}", "?");
        }
        return new BondSql(sql, parameterMappings);
    }

    /**
     * 递归查找 #{}
     *
     * @param sql select * from user where id = #{id} and userName = #{userName}
     */
    private static void parserSql(String sql) {
        //找到第一个#{
        int openIndex = sql.indexOf("#{", findPosition);
        //是否找到
        if (openIndex != -1) {
            //再找}
            int endIndex = sql.indexOf("}", openIndex);
            //找到了就记录在map中开始标签和结束标签的位置
            if (endIndex != -1) {
                map.put(openIndex, endIndex);
                //从结束标签的下一个位置继续找
                findPosition = endIndex + 1;
                //递归
                parserSql(sql);
            } else {
                System.out.println("SQL语句中参数错误...");
            }
        }
    }

    public static void main(String[] args) {
        int f = 0;
        String sql = "select * from user where id = #{id} and userName = #{userName}";
        BondSql bondSql = getBoundSql(sql);
        System.out.println(bondSql);
    }
}
